package mop.model;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import mop.controller.UseCaseController;
import mop.exception.IllegalIdException;
import mop.exception.IllegalUserException;
import mop.model.project.Project;
import mop.model.resource.Resource;
import mop.model.resource.ResourceType;
import mop.model.task.Task;
import mop.model.task.tasktype.TaskType;
import mop.model.user.User;
import mop.model.user.UserType;
import mop.security.acl.Acl;
import mop.security.acl.AclInterface;

/**
 * System
 * 
 * The world class containing what's global to the system.
 * @author MOP Team 7
 */
public class World
{
	
	/**
	 * Creates the System class.
     *
     * @effect 	a new ProjectDataBase is made
     * 		   	a new UserDataBase is made
     * 		  	a new ResourceDataBase is made
	 */
	public World()
	{
		this.projectDataBase = new DataBase<Project>();
		this.userDataBase = new DataBase<User>();
		this.resourceDataBase = new DataBase<Resource>();
		this.userTypeDataBase = new DataBase<UserType>();
		this.taskTypeDataBase = new DataBase<TaskType>();
		this.resourceTypeDataBase = new DataBase<ResourceType>();
	}

	
	/***************************************************************************
	 * 							Project DataBase
	 ***************************************************************************/
		
	/**
	 * The project database.
	 */
	private final DataBase<Project> projectDataBase;
	
	/**
	 * Inspector for the project database.
	 * 
	 * @return	this.projectDatabase
	 */
	public DataBase<Project> getProjectDataBase()
	{
		return this.projectDataBase;
	}
	
	/***************************************************************************
	 * 							User DataBase
	 ***************************************************************************/
	
	/**
	 * Inspector for the user database.
	 * 
	 * @return 	this.userDataBase
	 */
	public DataBase<User> getUserDataBase()
	{
		return this.userDataBase;
	}
	
	/**
	 * The user database.
	 */
	private final DataBase<User> userDataBase;
	
	/***************************************************************************
	 * 							Resource DataBase
	 ***************************************************************************/

	/**
	 * Inspector for the resource database.
	 * 
	 * @return 	this.resourceDataBase
	 */
	public DataBase<Resource> getResourceDataBase()
	{
		return this.resourceDataBase;
	}
	
	/**
	 * The resource database.
	 */
	private final DataBase<Resource> resourceDataBase;
	
	
	/***************************************************************************
	 * 							TaskType DataBase
	 ***************************************************************************/
	
	/**
	 * Inspector for the tasktype database.
	 * 
	 * @return 	this.TaskTypeDataBase
	 */
	public DataBase<TaskType> getTaskTypeDataBase()
	{
		return this.taskTypeDataBase;
	}
	
	/**
	 * The task type database.
	 */
	private final DataBase<TaskType> taskTypeDataBase;
	
	
	/***************************************************************************
	 * 							ResourceType DataBase
	 ***************************************************************************/
	
	/**
	 * Inspector for the resourcetype database.
	 * 
	 * @return 	this.resourceTypeDataBase
	 */
	public DataBase<ResourceType> getResourceTypeDataBase()
	{
		return this.resourceTypeDataBase;
	}
	
	/**
	 * The task type database.
	 */
	private final DataBase<ResourceType> resourceTypeDataBase;
	
	
	/***************************************************************************
	 * 							UserType DataBase
	 ***************************************************************************/
	
	/**
	 * Inspector for the usertype database.
	 * 
	 * @return 	this.userTypeDataBase
	 */
	public DataBase<UserType> getUserTypeDataBase()
	{
		return this.userTypeDataBase;
	}
	
	/**
	 * The task type database.
	 */
	private final DataBase<UserType> userTypeDataBase;
	
	
	/***************************************************************************
	 * 							CurrentUser
	 ***************************************************************************/
	
	/**
	 * Returns the current user.
	 * 
	 * @return	The current user.
	 */
	public User getCurrentUser()
	{
		return this.currentUser;
	}
	
	/**
	 * Sets the current user.
	 * Accessible only on "package-private" level.
	 * 
	 * @param 	newCurrentUser the new current user
	 * @throws IllegalUserException 
	 * 		| if the new suggested user could not be found in database
	 */
	public void setCurrentUser(User newCurrentUser) throws IllegalIdException 
	{
		if(userDataBase.contains(newCurrentUser))
		{
			this.currentUser = newCurrentUser;
		}
		else
		{
			throw new IllegalIdException(newCurrentUser.getDescription());
		}
	}
	
	/**
	 * The current User of the system.
	 */
	private User currentUser = null;
	
	/***************************************************************************
	 * 						Change clock functionality
	 ***************************************************************************/
	/**
	 * Variable representing the difference in milliseconds from virtual & real time
	 */
	private static long timeDiff = 0;
	
	/**
	 * Change the virtual date & time
	 * @param time	Date & time timestamp
	 * @post difference with virtual time is set
	 * 	|getTimeDiff() == time.getTime() - Calender.getInstance().getTimeInMillis()
	 */
	public static void setVirtualTime(Timestamp time)
	{
		timeDiff = time.getTime() - Calendar.getInstance().getTimeInMillis();
	}
	
	/**
	 * Get the difference in milliseconds from virtual and real time
	 * @return	hours
	 */
	public static long getTimeDiff()
	{
		return World.timeDiff;
	}
	
	/**
	 * Get the virtual current time
	 * @return	virtual current time
	 */
	public static Date getTime()
	{ 
		Calendar c = Calendar.getInstance();
		return new Timestamp(c.getTimeInMillis() + getTimeDiff());
	}
	
	/***************************************************************************
	 * 							ACL functionality
	 ***************************************************************************/
	public void setAcl(Acl acl){
		this.acl = acl;
	}
	
	public AclInterface getAcl(){
		return acl;
	}
	
	private Acl acl;
	/***************************************************************************
	 * 							Controller functionality
	 ***************************************************************************/
	
	public void setControllers(Collection<UseCaseController> controllers){
		List<UseCaseController> controller = new LinkedList<UseCaseController>();
		controller.addAll(controllers);
		this.controllers = controller;
	}
	
	public List<UseCaseController> getAvailableControllers(){
		List<UseCaseController> available = new LinkedList<UseCaseController>();
		for(UseCaseController c : controllers){
			if(acl.isAllowed(getCurrentUser(), c))
				available.add(c);
		}
		return available;
	}
	
	private List<UseCaseController> controllers;
	
	
	
	/***************************************************************************
	 * 							Global functionality
	 ***************************************************************************/

	/**
	 * Return all existing tasks
	 * 
	 * @return all tasks of all users in the user database
	 */
	public List<Task> getAllTasks()
	{
		List<Task> giantList = new LinkedList<Task>();
		
		for(User u : this.userDataBase.fetchAll())
		{
			giantList.addAll(u.getTasks());
		}
		
		return giantList;
	}
	
	/**
	 * Find a task with given id in all existing tasks
	 * 
	 * @param	id	ID of the task
	 * @return 	Task when it exists, false otherwise
	 * @throws 	IllegalIdException 
	 * 			Thrown when the given task does not exist in the database.
	 */
	public Task findTask(String id) 
		throws IllegalIdException
	{
		List<Task> tasks = getAllTasks();
		for(Task t : tasks){
			if(t.getId().equals(id))
				return t;
		}
		
		throw new IllegalIdException(id);
	}
	
}